/* 
 * File:   Directory.cpp
 * Author: Usama Shaikh
 * 
 * Created on February 20, 2011, 12:00 PM
 */

#include "Directory.h"
#include "FreeList.h"

using namespace std;


Directory::Directory(FreeList* fl, bool createNew){
    printf("Directory::Directory :");
    m_freelist = fl;
    m_numDirEntries = 0;
    m_bg = NULL;
    if(createNew){
        m_bg = m_freelist->CreateNew();
        printf("Start block number: %d",m_bg->GetStartBlockNumber());
        if(m_bg != NULL)
            printf("Create New successful\n");
        writeDirToMaster();
    }
    else{
        Block mb(0,fl->GetDisk());
        int start = mb.GetPointer(Block::DIRECTORY_START_BLOCK_NUMBER);
        int end = mb.GetPointer(Block::DIRECTORY_END_BLOCK_NUMBER);
        int num = mb.GetPointer(Block::DIRECTORY_NUMBER_OF_BLOCKS);
        if(start ==-1 || end == -1 || num == 0){
            printf("Failed. No Directory available on Disk\n");
            return;
        }

        m_bg = new BlockGroup
              ( mb.GetPointer(Block::DIRECTORY_START_BLOCK_NUMBER),
                mb.GetPointer(Block::DIRECTORY_END_BLOCK_NUMBER),
                mb.GetPointer(Block::DIRECTORY_NUMBER_OF_BLOCKS),
                fl);
        m_numDirEntries = mb.GetPointer(Block::NUMBER_OF_DIRECTORY_ENTRIES);
        
        if(m_bg != NULL){
            printf("Open existing successful\n");
            printf("start Block Number: %d\n",m_bg->GetStartBlockNumber());
            printf("end Block Number: %d\n",m_bg->GetEndBlockNumber());
            printf("Blocks: %d\n",m_bg->GetNumberOfBlocks());
            printf("Directories: %d\n",m_numDirEntries);

        }
    }
    if(m_bg == NULL) printf("Fail!\n");
}


Directory::~Directory() {
    m_directoryEntries.clear();
    printf("Clear Successful\n");
}

void Directory::Open(){
    printf("Directory::Open :");
    m_bg->Rewind();

    if(m_bg == NULL){
        printf("Error: Directory not initialized\n");
        return;
    }
     if(m_bg->GetCurrentBlock() == NULL){
        printf("Error: No block in blockgroup\n");
        return;
    }
    int numBlocks = m_bg->GetNumberOfBlocks();
    printf("Num of blocks = %d\n",numBlocks);

    for(int i = 0; i< numBlocks; i++){

        unsigned char* buffer = m_bg->GetCurrentBlock()->GetBuffer();
        //string a = (string)(const char*) buffer;

        int entries_left = m_numDirEntries-i*DIRECTORY_ENTRIES_PER_BLOCK;
        int entries_this_block = (DIRECTORY_ENTRIES_PER_BLOCK
        < entries_left)?
           DIRECTORY_ENTRIES_PER_BLOCK:entries_left ;//23/21= 1; 23%21 = 2

        for(int j = 0; j <entries_this_block;j++){
            //fileeName: 4+4,4+24+4,4+48+4
            //FCB loc: 1,7, 13..
            int fNameIdx =  sizeof(int)
                            + j*(DirectoryEntry::FILENAME_LENGTH+sizeof(int))
                            + sizeof(int);

            int fcb = m_bg->GetCurrentBlock()->GetPointer(1+j*6);
            char name[DirectoryEntry::FILENAME_LENGTH];
            //strcpy()

            for(int k = 0; k<DirectoryEntry::FILENAME_LENGTH ; k++){
                name[k] = buffer[k+fNameIdx] ;
            }
            //printf("FileName: %s\n",name);
            //dr.SetFileName((char*)a.substr(fNameIdx,DirectoryEntry::FILENAME_LENGTH).data());
            DirectoryEntry dr(name,fcb);
            //printf("Dir Entry: %s, %d\n",dr.GetFileName(),dr.GetFileControlBlock());
            m_directoryEntries.push_back(dr);
//            DirectoryEntry dri = m_directoryEntries.back();//[m_directoryEntries.size()-1];
         //   printf("Dir Entry: %s, %d\n",dri.GetFileName(),dri.GetFileControlBlock());
            


        }

        m_bg->GetNextBlock();
        if(m_bg->GetCurrentBlock() == NULL){ //break;
            i = m_bg->GetNumberOfBlocks(); //set it up for exit
            printf("Success!\n Read %d blocks and %d entries\n",
                    m_bg->GetNumberOfBlocks(),m_directoryEntries.size());
            break;
        }
    }
    m_bg->Rewind();
}

vector<DirectoryEntry> Directory::List(){
    printf("Directory:: List: (File Name: FCB)\n");
    for(unsigned int i = 0; i < m_directoryEntries.size(); i++){
//        printf("%s, %d\n", m_directoryEntries[i].GetFileName(),
//                m_directoryEntries[i].GetFileControlBlock());
          DirectoryEntry dri = m_directoryEntries[i];
           // printf("Dir Entry: %s, %d\n",dri.GetFileName(),dri.GetFileControlBlock());

    }
    return m_directoryEntries;

}

int Directory::Find(char* fileName){

    for(int i = 0; i < m_directoryEntries.size();i++){
        if(strcmp(m_directoryEntries[i].GetFileName(),fileName) == 0){
           return m_directoryEntries[i].GetFileControlBlock();
       }
   }
     return -1;
}

bool Directory::Flush(){
    //cout << "Directory::Flush: ";
    bool result = false;
    int blocksNeeded = m_numDirEntries / DIRECTORY_ENTRIES_PER_BLOCK;
    if(m_numDirEntries % DIRECTORY_ENTRIES_PER_BLOCK !=0)
        blocksNeeded++;
            //22/21 + 22%21=2
    //21/21 
//    printf("Blocks available %d \n",m_bg->GetNumberOfBlocks());
    if(blocksNeeded > m_bg->GetNumberOfBlocks())
        do{
            m_bg->AddBlock();
        }while(blocksNeeded != m_bg->GetNumberOfBlocks());

        //at least keep one block!!!
    else if(blocksNeeded < m_bg->GetNumberOfBlocks()
            && m_bg->GetNumberOfBlocks() > 1)
        do{
            Block * b = m_bg->UnlinkBlock();//to avoid memory leaks
            delete b;
        }while(blocksNeeded != m_bg->GetNumberOfBlocks()
                && m_bg->GetNumberOfBlocks() > 1);

//        printf("Blocks Needed: %d, Blocks available:%d \n",
//                blocksNeeded,m_bg->GetNumberOfBlocks());
//        printf("Start bnumber: %d,End B:%d\n",
//                m_bg->GetStartBlockNumber(),m_bg->GetEndBlockNumber());
    m_bg->Rewind();
    int entriesWritten = 0;
    //printf("rewind\n");
    for(int i = 0; i<m_bg->GetNumberOfBlocks(); i++){
        //printf("Blocks Loop entered\n");
        //Block b(m_start)
        int entries_left = m_numDirEntries-i*DIRECTORY_ENTRIES_PER_BLOCK;
        int entries_this_block = (DIRECTORY_ENTRIES_PER_BLOCK
        < entries_left)?
           DIRECTORY_ENTRIES_PER_BLOCK:entries_left ;//23/21= 1; 23%21 = 2

      //  printf("Directories for this block: %d\n",entries_this_block);
        for(unsigned int j = 0;
                j<(unsigned int) entries_this_block; j++){
        //    printf("Directory Entries / Block loop entered\n");
        
            int fcb = m_directoryEntries[entriesWritten].GetFileControlBlock();
            int fNameIdx =  sizeof(int)
                            + j*(DirectoryEntry::DIRECTORY_ENTRY_LENGTH)
                            + sizeof(int);

            m_bg->GetCurrentBlock()->SetPointer(fcb,1+j*6);       
            
            unsigned char* fname =
                (unsigned char*)m_directoryEntries[entriesWritten].GetFileName();
            unsigned char* buf =
                m_bg->GetCurrentBlock()->GetBuffer();//Gets 512 B
           // printf("fname : %s\n",fname);
            for(int k = 0; k<DirectoryEntry::FILENAME_LENGTH ; k++){
                buf[k+fNameIdx] = fname[k] ;
            }
            entriesWritten++;
        }
        m_bg->GetCurrentBlock()->Write(m_bg->GetDisk());
        cout << "wrote to block " << m_bg->GetCurrentBlock()->GetBlockNumber() << " on disk" << endl;
        m_bg->GetNextBlock();
        if(m_bg->GetCurrentBlock() == NULL){//reached end of loop
            //printf("%d blocks written to disk\n",i+1);
            cout << i+1 << " blocks written to disk" << endl;
            m_bg->Rewind();
        }
    }
    result = writeDirToMaster();

    return result;
}

bool Directory::Delete(char* fileName){
    printf("Directory::Delete :");
     bool res = false;
     vector<DirectoryEntry>::iterator cii = m_directoryEntries.begin();

     for(unsigned int i = 0;i < m_directoryEntries.size(); i++){
         cii++;
         if(strcmp(m_directoryEntries[i].GetFileName(),fileName) == 0){
          printf("Found %s; FCB=%d",fileName,
                   m_directoryEntries[i].GetFileControlBlock());
           m_directoryEntries.erase(cii-1);
           res = true;
           m_numDirEntries--;
       }
   }
     return res;
}

bool Directory::Add(char* fileName, int FCB){
    printf("Directory::Add : %s,%d\n",fileName,FCB);
    if(m_numDirEntries >=
            DIRECTORY_ENTRIES_PER_BLOCK*(FreeList::DEFAULT_NUMBER_OF_BLOCKS-1)){
        printf("Max Directory Entries reached. Can't Add more\n");
        return false;
    }

    DirectoryEntry dr(fileName,FCB);
    m_directoryEntries.push_back(dr);
    m_numDirEntries++;
    printf("Success!\n");
    return true;
}

bool Directory::Rename(char* fileName, char* newName){
     //vector<DirectoryEntry>::const_iterator cii;
    printf("Directory::Rename: ");
     bool res = false;
     
     for(unsigned int i = 0; i< m_directoryEntries.size(); i++){
       if(strcmp(m_directoryEntries[i].GetFileName(), fileName) == 0){
           printf("Found fileName at location %d\n",i);
           m_directoryEntries[i].SetFileName(newName);
           res = true;
         }
     }
     if(!res) printf("%s not found\n",fileName);
     return res;
}

bool Directory::Clear(){
    m_directoryEntries.clear();
    m_numDirEntries = 0;
    return true;
}

bool Directory::DeleteDirectory(){
    bool res = false;
    Clear();
    m_freelist->Return(m_bg);
    writeDirToMaster();
    delete m_bg;
    res = true;
    return res;
}

bool Directory::writeDirToMaster(){
    Block master(0,m_freelist->GetDisk());
    master.SetPointer(m_bg->GetStartBlockNumber(),Block::DIRECTORY_START_BLOCK_NUMBER);
    master.SetPointer(m_bg->GetEndBlockNumber(),Block::DIRECTORY_END_BLOCK_NUMBER);
    master.SetPointer(m_bg->GetNumberOfBlocks(),Block::DIRECTORY_NUMBER_OF_BLOCKS);
    master.SetPointer(m_numDirEntries,Block::NUMBER_OF_DIRECTORY_ENTRIES);

    return master.Write(m_freelist->GetDisk());
}
void Directory::Test(){

}
